package cn.kgc.advice;

import cn.kgc.annotation.Logical;
import cn.kgc.annotation.LoginType;
import cn.kgc.annotation.RequireLogin;
import cn.kgc.base.SystemConstant;
import cn.kgc.excpetion.GraceException;
import cn.kgc.grace.result.ResponseStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @author YC
 * 验证令牌中的登录类型是否与控制器添加的注解标记的允许类型一致
 */
@Slf4j
@Aspect
@Component
public class LoginPermissionAdvice {

    @Pointcut("@within(org.springframework.web.bind.annotation.RestController) || @within(org.springframework.stereotype.Controller)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获得目标方法
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        // 获得用户请求
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            String loginType = attributes.getRequest().getHeader(SystemConstant.HEAD_LOGIN_TYPE);
            if (!checkLoginPermission(method, loginType)) {
                GraceException.display(ResponseStatusEnum.UN_LOGIN);
            }
        }
        // 否则请求放行
        return joinPoint.proceed();
    }

    private boolean checkLoginPermission(Method method, String loginType) {
        if (!method.isAnnotationPresent(RequireLogin.class)) {
            return true;
        }
        RequireLogin annotation = method.getAnnotation(RequireLogin.class);
        LoginType[] value = annotation.value();
        Logical logical = annotation.Logical();
        if (logical == Logical.AND) {
            //登录令牌的登录类型(方法中的传参loginType)要与方法注解上指定的类型都匹配,请求才能被访问。
            return Arrays.stream(value).allMatch(type -> type.getValue().equals(loginType));
        } else if (logical == Logical.OR) {
            //登录令牌的登录类型(方法中的传参loginType)要与方法注解上指定的类型中任意一个匹配,请求就能被访问。
            return Arrays.stream(value).anyMatch(type -> type.getValue().equals(loginType));
        }
        return false;
    }

}
